/*
  Meanwhile - Unofficial Lotus Sametime client library
  
  Copyright (c) 2007 Christopher O'Brien <siege@preoccupied.net>
  
  License: LGPL
  
  This library is free software; you can redistribute it and/or modify
  it under the terms of version 2 the GNU Library General Public
  License as published by the Free Software Foundation.
                                                                                
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA
*/


#include "mw-mpi.h"


/* this can optionally be compiled so that it will use libgcrypt or
   libgmp instead of the built-in copy of libmp */


#if USE_GCRYPT
#include <sys/select.h>
#include <gcrypt.h>

#elif USE_GMP
#include <gmp.h>

#else
#include "mpi/mpi.h"
#include <time.h>

#endif


/* the VAL macro gets us the correct type for the mpi functions from
   the mw_mpi structure */
#ifdef VAL
#undef VAL
#endif



#if USE_GCRYPT
struct mw_mpi {
  gcry_mpi_t val;
};

#define VAL(m) (m->val);

#elif USE_GMP
struct mw_mpi {
  mpz_t val;
};

#define VAL(m) (&m->val);

#else
struct mw_mpi {
  mw_mp_int val;
};

#define VAL(m) (&m->val);

#endif



static void mw_mpi_init(MwMPI *m) {
#if USE_GCRYPT
  (VAL(m)) = gcry_mpi_new(512);

#elif USE_GMP
  mpz_init(VAL(m));

#else
  mw_mp_init(VAL(m));

#endif
}



MwMPI *mw_mpi_new() {
  MwMPI *n = g_new0(MwMPI, 1);
  mw_mpi_init(n);
  return n;
}



MwMPI *mw_mpi_dup(const MwMPI *m) {
  MwMPI *n = mw_mpi_new();
  mw_mpi_set(n, m);
  return n;
}


static void mw_mpi_clear(MwMPI *m) {
#if USE_GCRYPT
  gcry_mpi_release(VAL(m));
  (VAL(m)) = NULL;

#elif USE_GMP
  mpz_clear(VAL(m));

#else
  mw_mp_clear(VAL(m));

#endif
}


void mw_mpi_free(MwMPI *m) {
  if(m) {
    mw_mpi_clear(m);
    g_free(m);
  }
}



void mw_mpi_import(MwMPI *m, const guchar *buf, gsize len) {
#if USE_GCRYPT
  mw_mpi_clear(m);
  gcry_mpi_scan(VAL(m), GCRYMPI_FMT_USG, buf, len, NULL);

#elif USE_GMP
  mpz_import(VAL(m), len, 1, 1, 0, 0, buf);

#else
  mw_mpi_read_raw(VAL(m), buf, len);

#endif
}



void mw_mpi_export(const MwMPI *m, guchar **buf, gsize *len) {
#if USE_GCRYPT
  gcry_mpi_aprint(GCRYMPI_FMT_USG, buf, len, VAL(m));

#elif USE_GMP
  *len = (mpz_sizeinbase(VAL(m), 2) + 7) / 8;
  *buf = g_malloc(*len);
  mpz_export(*buf, NULL, 1, 1, 1, 0, VAL(m));
  
#else
  *len = mw_mp_raw_size(VAL(m));
  *buf = g_malloc(*len);
  mw_mp_toraw(VAL(m), *buf);
  
#endif
}


void mw_mpi_set(MwMPI *to, const MwMPI *from) {
#if USE_GCRYPT
  VAL(to) = gcry_mpi_set(VAL(to), VAL(from));

#elif USE_GMP
  mpz_set(VAL(to), VAL(from));

#else
  mw_mp_copy(VAL(from), VAL(to));

#endif
}


void mw_mpi_setul(MwMPI *to, gulong l) {
#if USE_GCRYPT
  VAL(to) = gcry_set_ui(VAL(to), l);

#elif USE_GMP
  mpz_set_ui(VAL(to), l);
  
#else
  mw_mp_set_int(VAL(to), l);

#endif
}


void mw_mpi_set_random(MwMPI *m, guint bits) {
#if USE_GCRYPT
  gcry_mpi_randomize(VAL(m), bits, GCRY_WEAK_RANDOM);

#elif USE_GMP
  gmp_randstate_t rstate;
  gmp_randinit_default(rstate);
  mpz_urandomb(VAL(m), rstate, bits);
  gmp_randclear(rstate);

#else
  /* no built-in random, so we'll make up our own. Allocate the right
     amount of memory, and assign each byte in turn randomly, then
     mask off the unnecessary bits, and load it into m. */
  gsize len, l;
  guchar *buf;

  l = len = (bits / 8) + 1;
  buf = g_malloc(len);
  srand(clock() + m);
  while(l--) buf[l] = rand() & 0xff;
  buf[0] &= (0xff >> 8 - (bits % 8));
  mw_mp_read_raw(VAL(m), buf, len);
  g_free(buf);

#endif
}


void mw_mpi_modpw(MwMPI *result, const MwMPI *i, const MwMPI *pow,
		  const MwMPI *mod) {
#if USE_GCRYPT
  gcry_mpi_powm(VAL(result), VAL(i), VAL(pow), VAL(mod));

#elif USE_GMP
  mpz_powm(VAL(result), VAL(i), VAL(pow), VAL(mod));

#else
  mw_mp_exptmod(VAL(i), VAL(pow), VAL(mod), VAL(result));

#endif
}


/* The end. */
